﻿using DGZImp.BasProject.DomainModels;
using DGZImp.Core.DbSqlSugar;
using DGZImp.Core.EFDbContext;
using DGZImp.Core.Exceptions;
using DGZImp.Core.Extensions;
using DGZImp.Core.ManageUser;
using DGZImp.Core.Utilities;
using DGZImp.ApsProject.DomainModels;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Dm.Config;
using System.Text.RegularExpressions;
using System.Text;
using System.Net.Http.Json;
using DGZImp.WmsProject.Dtos;
using DGZImp.ApsProject.IServices;
using SqlSugar;
using System.Net.NetworkInformation;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using DGZImp.ApsProject.Dtos;
using System.Reflection.Metadata.Ecma335;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using DGZImp.Core.BaseProvider;
using System.Net.Sockets;
using Microsoft.AspNetCore.Hosting;
using static System.Net.Mime.MediaTypeNames;
using System.Reflection.PortableExecutable;
using DGZImp.BasProject.IServices;

namespace DGZImp.WmsProject.Services
{

    /// <summary>
    /// 排程
    /// </summary>
    public class ScheduleService : IDependency, IScheduleService
    {
        public ISqlSugarClient Db = AutofacContainerModule.GetService<ServiceDbContext>()?.SqlSugarClient;
        public IBas_ParameterService ParamSer = AutofacContainerModule.GetService<IBas_ParameterService>();

        public UserInfo userInfo = UserContext.Current.UserInfo;

        public ILogger<ScheduleService> logger { get; set; }

        //private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        public IConfiguration _config;


        public ScheduleService(IConfiguration configuration, IHttpClientFactory httpClientFactory)
        {
            _config = configuration;
        }




        public async Task<string> SyncAutoScheduling()
        {
            var list = await Db.Queryable<Aps_Mo>()
                .LeftJoin<Aps_SalesOrder>((a, b) => a.SalesOrderId == b.SalesOrderId)
                .LeftJoin<Bas_Material>((a, b, c) => b.MaterialId == c.MaterialId && c.IsValid == 1)
                .Where((a, b) => a.IsValid == 1 && (a.Status != 1 && a.Status != 8) && b.IsValid == 1)
                 .Where((a, b) => a.DeadLine != null)
                .Select((a, b, c) => new ApsMo
                {
                    SalesOrderId = a.SalesOrderId,
                    RequiredDate = b.RequiredDate,
                    SalesMaterialId = b.MaterialId,
                    TotalQty = b.TotalQty,
                    MoCode = a.MoCode,
                    MoId = a.MoId,
                    Qty = a.Qty,
                    DeadLine = a.DeadLine,
                    ProductsCode = a.ProductsCode,
                    MaterialId = a.MaterialId,
                    Status = a.Status,
                    FinishedQty = a.FinishedQty,
                    LotNo = a.LotNo,
                    Level = a.Level,
                    IsCe = c.IsCe
                })
                .ToListAsync();

            // 未齐套的放到最后todo


            var moCodes = list.Select(x => x.MoCode).ToList();

            var newVersion = Db.Queryable<Aps_ScheduleVersion>()
                .Where(x => x.IsValid == 1 && x.IsEdit == 1)
                .First();

            var scheduleds = new List<Aps_ScheduleTask>();
            if (newVersion != null)
                scheduleds = await Db.Queryable<Aps_ScheduleTask>()
                   //.LeftJoin<Aps_SalesOrder>((a, b) => a.SalesOrderId == b.SalesOrderId)
                   .Where(a => a.IsValid == 1 && a.ScheduleVersionId == newVersion.ScheduleVersionId)
                   .ToListAsync();

            var scheduledCodes = scheduleds.Select(x => x.MoCode).ToList();
            //已经排程的工单中去除掉已完成的/删除的工单
            scheduleds = scheduleds.Where(a => moCodes.Contains(a.MoCode)).ToList();
            var calScheduleds = await ReCalculteScheduledTask(scheduleds);

            //去除掉已排程的工单找新的工单
            var newList = list.Where(a => !scheduledCodes.Contains(a.MoCode)).ToList();
            newList.ForEach(item =>
            {
                item.IsNew = 1;
            });

            var priority = 1;
            //排程规则类型
            var param = await ParamSer.QueryParamIniByNameAndType("Priority", 5);
            if (param != null && param.Count > 0)
            {
                priority = Convert.ToInt32(param[0].ParamValue);
            }
            var newListGroup = new List<ApsMoGroup>() { };

            if (priority != 1 && priority != 2) throw new AppException($"不支持排程规则：{priority}，请到系统参数配置正确的排程优先级 ");
            //按照销售单分组，根据订单交货日及需求数量优先级分组
            newListGroup = newList.GroupBy(a => new { a.SalesOrderId, a.SalesMaterialId }).Select(g => new ApsMoGroup
            {
                SalesOrderId = (long)g.Key.SalesOrderId,
                SalesMaterialId = g.Key.SalesMaterialId,
                RequiredDate = g.Min(a => a.RequiredDate),
                TotalQty = g.Max(a => a.TotalQty),
                Mos = g.ToList()
            }).ToList();
            if (priority == 1)
            {
                newListGroup = newListGroup.OrderBy(a => a.RequiredDate).ThenByDescending(a => a.TotalQty).ToList();
            }
            else
            {
                newListGroup = newListGroup.OrderBy(a => a.RequiredDate).ThenBy(a => a.TotalQty).ToList();
            }

            //找到所有新的工单的工艺及设备
            var preNewSchedules = await FindMoRoute(newListGroup);

            preNewSchedules = preNewSchedules.OrderBy(a => a.MoSeq).ThenBy(a => a.StepSequence).ThenBy(a => a.DeadLine).ToList();

            //工单排到设备
            var result = await CalculateSchedule(preNewSchedules);


            calScheduleds.reqTask.AddRange(result.reqTask);
            calScheduleds.reqDate.AddRange(result.reqDate);

            //5.插入排程记录、排程任务、排程时间
            Db.Ado.BeginTran();
            try
            {
                //将上版本排程记录更新为不可编辑
                await Db.Updateable<Aps_ScheduleVersion>()
                       .SetColumns(x => x.ModifyId == userInfo.UserId)
                       .SetColumns(x => x.Modifier == (string.IsNullOrEmpty(userInfo.UserName) ? null : userInfo.UserName))
                       .SetColumns(x => x.ModifyTime == DateTime.Now)
                       .SetColumns(x => x.IsEdit == 0)
                       .Where(x => x.IsEdit == 1)
                       .ExecuteCommandAsync();
                var versionId = new Core.Utilities.IdWorker().NextId();
                //创建新版本排程记录
                await Db.DoInsertAsync(new Aps_ScheduleVersion { ScheduleVersionId = versionId, Version = DateTime.Now.ToString("yyMMddHHmmss"), IsEdit = 1 });

                calScheduleds.reqTask.ForEach(v => v.ScheduleVersionId = versionId);
                calScheduleds.reqDate.ForEach(v => v.ScheduleVersionId = versionId);

                await Db.DoInsertAsync(calScheduleds.reqTask);

                await Db.DoInsertAsync(calScheduleds.reqDate);

                var orders = calScheduleds.reqTask.Select(a => a.MoCode).Distinct().ToList();
                //更新工单为已排程
                await Db.DoUpdateAsync<Aps_Mo>(new { IsSchedule = 1 }, x => x.IsValid == 1 && orders.Contains(x.MoCode));

                Db.Ado.CommitTran();

            }
            catch (Exception ex)
            {

                Db.Ado.RollbackTran();

                throw new AppException(ex.Message);

            }

            return "";
        }


        public async Task<ResFirstCulationDto> ReCalculteScheduledTask(List<Aps_ScheduleTask> lst)
        {
            var data = new ResFirstCulationDto() { reqDate = new List<Aps_ScheduleTaskDate>() { }, reqTask = new List<Aps_ScheduleTask>() { } };
            var deviceTasks = lst.GroupBy(a => new { a.MachineId, a.MachineLevel }).Select(a => new
            {
                MachineId = a.Key.MachineId,
                MachineLevel = a.Key.MachineLevel,
                Details = a.OrderBy(g => g.DeviceSeq).ToList()
            }).OrderBy(a => a.MachineLevel).ToList();


            var salesCodes = lst.Select(a => a.MoCode).ToList();

            var mos = await Db.Queryable<Aps_Mo>()
                .Where(a => salesCodes.Contains(a.MoCode))
                .Where(a => a.IsValid == 1)
                .ToListAsync();
            var routeDeviceIds = lst.Select(a => a.RouteDeviceId).ToList();
            var routeDevices = await Db.Queryable<Bas_RouteDevice>()
                .Where(a => routeDeviceIds.Contains(a.RouteDeviceId))
                .Where(a => a.IsValid == 1)
                .ToListAsync();

            var Seq = 0;

            var supMos = new List<Aps_ScheduleTask>() { };
            var tasks = new List<Aps_ScheduleTask>() { };
            var dates = new List<Aps_ScheduleTaskDate>() { };
            var list = new List<ApsMo>() { };
            foreach (var d in deviceTasks)
            {

                foreach (var item in d.Details)
                {

                    var mo = mos.Where(a => a.MoCode == item.MoCode).FirstOrDefault();
                    var routeDevice = routeDevices.Where(a => a.RouteDeviceId == item.RouteDeviceId).FirstOrDefault();

                    var device = new RountsDevices()
                    {
                        RouteControlId = item.RouteControlId,
                        RouteDeviceId = item.RouteDeviceId,
                        BeforeMinutes = item.BeforeMinutes,
                        PreparationMinutes = item.PreparationMinutes,
                        MachineId = item.MachineId,
                        GroupId = item.GroupId,
                        StepSequence = Seq,
                        Uph = routeDevice != null ? routeDevice.Uph : item.UPH,
                        OutputCircle = item.OutputCircle,
                        afterMinutes = item.AfterMinutes,
                        Enable = true
                    };

                    var moTmp = new ApsMo()
                    {

                        SalesOrderId = item.SalesOrderId,
                        RequiredDate = null,
                        SalesMaterialId = item.SalesMaterialId,
                        TotalQty = 0,
                        MoCode = item.MoCode,
                        Status = mo.Status,
                        Qty = item.Qty,
                        GroupId = item.GroupId,
                        DeadLine = mo != null ? mo.DeadLine : item.DeadLine,
                        ProductsCode = item.ProductsCode,
                        MaterialId = item.MaterialId,
                        FinishedQty = item.FinishedQty,
                        LotNo = item.LotNo,
                        Devices = new List<RountsDevices> { device }
                    };

                    Seq++;
                    list.Add(moTmp);
                }
            }
            var result = await this.CalculateSchedule(list, supMos);


            data.reqTask.AddRange(result.reqTask);
            data.reqDate.AddRange(result.reqDate);

            return data;
        }


        public async Task<List<ApsMo>> FindMoRoute(List<ApsMoGroup> list)
        {
            List<ApsMo> data = new List<ApsMo>() { };
            // 找到每个工单的工艺,设备及uph

            var products = list.Select(a => a.SalesMaterialId).Distinct().ToList();

            var routeUsage = await Db.Queryable<Bas_RouteUsage>()
                .LeftJoin<Bas_RouteName>((a, b) => a.RouteNameId == b.RouteNameId)
                .Where((a, b) => b.UseFlag == 1 && b.IsValid == 1)
                .Where(a => products.Contains(a.Sku) && a.IsValid == 1)
                .ToListAsync();

            var routeIds = routeUsage.Select(a => a.RouteNameId).ToList();

            var rountsDevices = await Db.Queryable<Bas_RouteControl>()
                .LeftJoin<Bas_RouteDevice>((a, b) => a.RouteControlId == b.RouteControlId)
                .Where((a, b) => routeIds.Contains(a.RouteNameId))
                .Where((a, b) => a.IsValid == 1 && b.IsValid == 1)
                .Select((a, b) => new RountsDevices
                {
                    RouteDeviceId = b.RouteDeviceId,
                    ProductStuffix = a.ProductStuffix,
                    RouteNameId = a.RouteNameId,
                    RouteControlId = a.RouteControlId,
                    MachineId = b.MachineId,
                    Uph = b.Uph,
                    OutputCircle = b.OutputCircle,
                    PreparationMinutes = b.PreparationMinutes,
                    BeforeMinutes = b.BeforeMinutes,
                    afterMinutes = b.AfterMinutes,
                    GroupId = b.GroupId,
                    StepSequence = a.StepSequence,
                    MachineLevel = b.MachineLevel,
                    IsCe = b.IsCe,
                    PositionRow = b.PositionRow,
                    RelatedDevice = b.RelatedDevice
                })
                .ToListAsync();

            var preSchdules = new List<ApsMo>() { };
            var mainIndex = 0;
            var materials = list.Select(a => a.SalesMaterialId).Distinct().ToList();
            var IsCeMaterial = Db.Set<Bas_Material>().Where(a => materials.Contains(a.MaterialId)).Select(a => new { a.MaterialId, a.IsCe }).ToList();
            foreach (var main in list)
            {
                mainIndex++;
                foreach (var item in main.Mos)
                {
                    var usageItem = routeUsage.Where(a => a.Sku == item.SalesMaterialId).ToList();
                    //var usageItem = routeUsage.Where(a => a.Sku == item.SalesMaterialId).FirstOrDefault();
                    var mat = item.ProductsCode.Split("/");
                    if (mat == null || mat.Count() < 1) continue;
                    // if (usageItem == null) continue;
                    if (usageItem.Count() <= 0) continue;
                    // var devices = rountsDevices.Where(a => a.RouteNameId == usageItem.RouteNameId && a.ProductStuffix == mat[1]).ToList();
                    var devices = rountsDevices.Where(a => usageItem.Any(b => a.RouteNameId == b.RouteNameId) && a.ProductStuffix == mat[1]).Distinct().ToList();
                    if (devices == null || devices.Count <= 0) continue;
                    item.Devices = devices;
                    item.StepSequence = devices[0].StepSequence;
                    item.GroupId = devices[0].GroupId;
                    item.MoSeq = mainIndex;
                    item.IsCe = IsCeMaterial.Where(a => a.MaterialId == item.SalesMaterialId).FirstOrDefault()?.IsCe ?? 0;
                    item.RouteNameId = usageItem[0].RouteNameId;
                    // 将同一个订单同一个工序的放到一个集合里面
                    item.Mos = main.Mos.Where(a => a.SalesOrderId == item.SalesOrderId && a.GroupId == item.GroupId).Select(a => new MoAndPreMo { MoCode = a.MoCode, PreMoCode = a.LotNo }).ToList();

                    data.Add(item);
                }

            }
            foreach (var item in data)
            {
                item.Mos = data.Where(a => a.SalesOrderId == item.SalesOrderId && a.GroupId == item.GroupId).Select(a => new MoAndPreMo { MoCode = a.MoCode, PreMoCode = a.LotNo }).ToList();
            }
            return data;
        }




        /// <summary>
        /// 工单排程
        /// </summary>
        /// <param name="lstOrder"></param>
        /// <param name="supMos"></param>
        /// <returns></returns>
        public async Task<ResFirstCulationDto> CalculateSchedule(List<ApsMo> lstOrder, List<Aps_ScheduleTask> supMos = null)
        {
            ResFirstCulationDto res = new ResFirstCulationDto();

            var lstTask = new List<Aps_ScheduleTask>() { };
            var lstTaskDate = new List<Aps_ScheduleTaskDate>() { };
            //
            DateTime begintime = DateTime.Now;


            var lstCalendar = Db.Queryable<Aps_PlantCalendar>()
                    .Where(a => a.WorkDate >= begintime.Date && a.IsValid == 1)
                    .Where(a => a.DateType == "W")
                    .OrderBy(a => a.WorkDate)
                    .OrderBy(a => a.ShiftType)
                    .ToList();

            var machines = lstOrder.SelectMany(a => a.Devices).Select(b => b.MachineId).ToList();
            var shiftIds = lstCalendar.Select(a => a.ShiftId).Distinct().ToList();
            var machineShiftDetails = Db.Queryable<Aps_MachineShift>()
                .Where(x => machines.Contains(x.MachineId) && x.WorkDate >= begintime.Date)
                .Where(x => shiftIds.Contains(x.ShiftId) && x.IsValid == 1)
                .Where(x => x.ClockInType == 1 || x.ClockInType == 3)
                .ToList();

            //supMos 存在表示是手动排程，可能存在其它工序也用到了这个设备但拖动的又不是这个工序
            //查找当前拖动的工单的所有的设备的排程结束时间,同时要去除--暂不考虑
            /* if (supMos != null && supMos.Count > 0)
             {
                 var handlOrders = lstOrder.Select(a => a.MoCode).ToList();
                 var handDevices = lstOrder.SelectMany(a => a.Devices).Select(b => b.MachineId).ToList();
                 Db.Queryable<Aps_ScheduleTask>()
                     .Where(a => handDevices.Contains(a.MachineId))
                     .Where(a => !handlOrders.Contains(a.MoCode))
             }*/

            var orderIndex = 0;
            var mainIndex = 0;
            var machineTimes = new List<MachineBeginTimeDto>() { };
            foreach (var lst in lstOrder)
            {
                var order = lst;
                if (lst.Mos != null && lst.Mos.Count() > 1) // -个订单相同工序多个工单 1.优先级: 前序工单的开始时间 + 产出节拍 最小的优先排。
                {
                    var curMos = new List<SeqMoDto>() { };
                    foreach (var mo in lst.Mos)
                    {

                        var tmp = lstTask.Where(a => a.MoCode == mo.PreMoCode).OrderByDescending(a => a.WorkingStartTime).FirstOrDefault();
                        if (tmp != null)
                        {
                            curMos.Add(new SeqMoDto() { MoCode = mo?.MoCode, PreMoCode = tmp?.MoCode, WorkingStartTime = tmp?.WorkingStartTime, OutputCircle = tmp?.OutputCircle });

                        }

                    }
                    curMos = curMos.OrderBy(a => a.WorkingStartTime.Value.AddMinutes((double)a.OutputCircle)).ToList();

                    foreach (var mo in curMos)
                    {
                        var tmp = lstTask.FirstOrDefault(a => a.MoCode == mo?.MoCode);
                        if (tmp == null)
                        {
                            order = lstOrder.Where(a => a.MoCode == mo?.MoCode).FirstOrDefault();
                            break;
                        }
                    }
                }
                if (lstTask.Any(a => a.MoCode == order.MoCode))
                {
                    continue;
                }
                // 如果是多个orders，那么先按照上一道序的开始时间+产出节拍 进行排序，然后给order赋值

                if (order.MoCode == "TEST02")
                {
                    var i = 0;
                }
                if (order.Status == 1 || order.Status == 8 || order.Status == null) continue;
                //剩余数量

                if (order.Qty - order.FinishedQty <= 0)
                    continue;




                //无行事历数据
                if (machineShiftDetails.Count <= 0) continue;
                if (order.Devices == null || order.Devices.Count <= 0) continue;
                if (string.IsNullOrEmpty(order.LotNo)) mainIndex++;
                var deviceIndex = 0;
                decimal moDeviceSurplus = (decimal)(order.Qty - (order.FinishedQty ?? 0));


                var curDevices = new Dictionary<long, decimal?>();
                //找到开始时间最早的设备及上一批次排的设备的位置，当前排的设备的位置要和上一个批次的位置相同
                if (supMos == null || supMos.Count <= 0)
                {
                    //含ce的产品只能排到含ce的设备上,不能含ce的产品只能排到不含ce设备上
                    var devices = new List<RountsDevices>() { };

                    // 如果产品也是含Ce 也要找含Ce的设备
                    if (order.IsCe != null && order.IsCe == 1)
                    {
                        devices = order.Devices.Where(a => a.IsCe == 1).ToList();
                    }
                    else
                    {
                        devices = order.Devices.Where(a => a.IsCe == 0 || a.IsCe == null).ToList();
                    }


                    var someLot = lstTask.Where(a => order.LotNo == a.LotNo).FirstOrDefault();
                    var subLot = lstTask.Where(a => order.LotNo == a.MoCode).FirstOrDefault();

                    if (someLot?.RelatedDevice != null) // 来源批次相同的优先排产到相邻的设备
                    {
                        curDevices = devices.Where(a => a.MachineId == someLot.RelatedDevice).ToDictionary(a => (long)a.MachineId, a => a.Uph);
                    }
                    else if (subLot != null && subLot.PositionRow != null) // 相同位置的排到同一个位置的设备
                    {
                        curDevices = devices.Where(a => a.PositionRow == subLot.PositionRow).ToDictionary(a => (long)a.MachineId, a => a.Uph);
                    }
                    else
                    {
                        curDevices = devices.ToDictionary(a => (long)a.MachineId, a => a.Uph);
                    }
                }
                else
                {
                    curDevices = order.Devices.ToDictionary(a => (long)a.MachineId, a => a.Uph);
                }



                //设备关联关系-批次相同的优先进入相邻压机，只要一个批次占用了一个压机，相邻压机其它批次都不能排

                //如果未找到设备则不排程
                if (curDevices == null) continue;

                var currentMachineShifts = machineShiftDetails
                    .Where(a => curDevices.ContainsKey((long)a.MachineId))
                    .Select(a => new curMachines
                    {
                        MachineId = a.MachineId,
                        StartTime = a.StartTime,
                        Uph = curDevices[(long)a.MachineId] // 添加Uph值
                    })
                    .OrderBy(a => a.StartTime)
                    .ToList();

                if (machineTimes.Count > 0)
                {
                    var isSchudeDevice = machineTimes.Where(a => curDevices.ContainsKey(a.MachineId)).ToList();
                    if (isSchudeDevice.Count > 0)
                    {

                        foreach (var item in isSchudeDevice)
                        {
                            //如果已经排过了，则删除原本的行事历。
                            currentMachineShifts.RemoveAll(shift => shift.MachineId == item.MachineId);

                            currentMachineShifts.Add(new curMachines
                            {
                                MachineId = item.MachineId,
                                StartTime = item.BeginTime,
                                Uph = curDevices[item.MachineId] // 添加Uph值
                            });
                        }
                    }
                }

                // 获取开始时间最早的或者产能最大的设备
                var shift = currentMachineShifts
                    .OrderBy(a => a.StartTime)
                    .ThenByDescending(a => a.Uph)
                    .FirstOrDefault();

                //无行事历
                if (shift == null) continue;

                var device = order.Devices.Where(a => a.MachineId == shift.MachineId).FirstOrDefault();



                //先查找之前的设备有没有生产过
                var machineProduce = machineTimes.Where(a => a.MachineId == device.MachineId).FirstOrDefault();

                if (machineProduce != null)
                {
                    begintime = machineProduce.BeginTime;
                }
                else
                {
                    begintime = shift.StartTime;
                }

                //找到上级工单的最小开始时间
                //查找上级批次要加上上一级工单节拍最小时长
                //第二台设备需不需要加上生产节拍--目前是加上生产节拍，因为不加上节拍则生产会比较早而上一级工单却还没有生产完
                if (supMos == null || supMos.Count <= 0)
                {
                    var beginTimeTmp = lstTask.Where(a => a.MoCode == order.LotNo).Min(a => a.WorkingStartTime);

                    var outputCircle = lstTask.Where(a => a.MoCode == order.LotNo).Min(a => a.OutputCircle);
                    if (beginTimeTmp != null && outputCircle != null && outputCircle > 0) beginTimeTmp = beginTimeTmp.Value.AddMinutes((double)outputCircle);
                    begintime = (DateTime)(beginTimeTmp > begintime ? beginTimeTmp : begintime);
                }
                else
                {
                    //手动排程
                    var beginTimeTmp = supMos.Where(a => a.MoCode == order.LotNo).Min(a => a.WorkingStartTime);
                    var outputCircle = supMos.Where(a => a.MoCode == order.LotNo).Min(a => a.OutputCircle);
                    if (beginTimeTmp != null && outputCircle != null && outputCircle > 0) beginTimeTmp = beginTimeTmp.Value.AddMinutes((double)outputCircle);
                    begintime = (DateTime)(beginTimeTmp > begintime ? beginTimeTmp : begintime);
                }

                //}

                //判断前置时间--如果是不同的产品则加上设备的准备时长（前面准备到开机）和前置时长（开机后准备模具等相关辅助工具材料）
                var maProduct = machineTimes.Where(a => a.MachineId == device.MachineId).FirstOrDefault();
                if (maProduct == null || (maProduct != null && order.ProductsCode != maProduct.ProductsCode))
                {
                    if (device.PreparationMinutes > 0) begintime = begintime.AddMinutes((double)device.PreparationMinutes);
                    if (device.BeforeMinutes > 0) begintime = begintime.AddMinutes((double)device.BeforeMinutes);
                }

                //创建排程任务，计算时间
                Aps_ScheduleTask apstask = new Aps_ScheduleTask();
                apstask.ScheduleTaskId = new Core.Utilities.IdWorker().NextId();
                apstask.MoCode = order.MoCode;
                apstask.PreMoCode = order.LotNo;
                apstask.GroupId = order.GroupId;
                apstask.LotNo = order.LotNo;
                apstask.MachineId = device.MachineId;
                apstask.OutputCircle = device.OutputCircle;
                apstask.PreparationMinutes = device.PreparationMinutes;
                apstask.BeforeMinutes = device.BeforeMinutes;
                apstask.AfterMinutes = device.afterMinutes;
                apstask.ProductsCode = order.ProductsCode;
                apstask.FinishedQty = order.FinishedQty;
                apstask.UPH = device.Uph;
                apstask.Qty = moDeviceSurplus;
                apstask.FinishedQty = order.FinishedQty;
                apstask.DeadLine = order.DeadLine;
                apstask.Remarks = order.Remarks;
                apstask.IsNew = order.IsNew;
                apstask.WorkingStartTime = begintime;
                apstask.MoSeq = mainIndex;
                apstask.Seq = 0;
                apstask.SalesMaterialId = order.SalesMaterialId;
                apstask.SalesOrderId = order.SalesOrderId;
                apstask.MaterialId = order.MaterialId;
                apstask.MachineLevel = device.MachineLevel;
                apstask.RouteDeviceId = device.RouteDeviceId;
                apstask.RouteControlId = device.RouteControlId;
                apstask.PositionRow = device.PositionRow;
                apstask.RelatedDevice = device.RelatedDevice;
                apstask.GroupSeq = order.StepSequence;
                apstask.RouteNameId = order.RouteNameId;
                //计算时间
                var result = CalculationDeviceTime(device, apstask, begintime, lstCalendar, machineShiftDetails, order.Devices);

                //工艺设备已经排过了，标识还有哪些设备可以排
                order.Devices.Where(a => a.RouteDeviceId == device.RouteDeviceId).First().Enable = false;

                //无生产数据--可能超期
                if (result.taskDates.Count <= 0) continue;

                if (result.IsClendarFinish == 1)
                {
                    apstask.WorkingEndTime = result.beginTime;

                }
                else
                {
                    //machineTimes.Where(a => a.MachineId == device.MachineId).First().Usable = false;
                    apstask.WorkingEndTime = apstask.WorkingStartTime.Value.AddHours(6);
                }
                //
                if (machineProduce != null)
                {
                    machineProduce.BeginTime = (DateTime)apstask.WorkingEndTime;
                    machineProduce.ProductsCode = order.ProductsCode;
                    machineProduce.DeviceSeq = machineProduce.DeviceSeq + 1;

                }
                else
                {
                    machineTimes.Add(new MachineBeginTimeDto
                    {
                        MachineId = (long)device.MachineId,
                        BeginTime = (DateTime)apstask.WorkingEndTime,
                        Usable = true,
                        ProductsCode = order.ProductsCode,
                        DeviceSeq = 0,

                    });
                }

                apstask.DeviceSeq = machineProduce != null ? machineProduce.DeviceSeq : 0;
                apstask.IsFinish = result.IsClendarFinish;

                apstask.Qty = moDeviceSurplus - result.Surplus;


                lstTask.Add(apstask);
                lstTaskDate = lstTaskDate.Concat(result.taskDates).ToList();


                moDeviceSurplus = (decimal)result.Surplus;


                // 如果有剩余说明当前设备到期没有排完则继续找下一个设备排程
                //if (result.Surplus <= 0) break;
                deviceIndex++;
                //}
                orderIndex++;
            }
            res.reqTask = lstTask;
            res.reqDate = lstTaskDate;
            return res;
        }


        public async Task<ReqGanttTableCalculate> HandSchedule(ReqHandSchedule req)
        {
            var salesCodes = req.MoList.Select(a => a.MoCode).ToList();
            //找到所有涉及到的工单
            var mos = await Db.Queryable<Aps_Mo>()
                .LeftJoin<Aps_SalesOrder>((a, b) => a.SalesOrderId == b.SalesOrderId)
                .Where((a, b) => salesCodes.Contains(a.MoCode) && a.IsValid == 1 && (a.Status != 1 && a.Status != 8) && b.IsValid == 1)
                // .Where((a, b) => a.DeadLine != null)
                .Select((a, b) => new ApsMo
                {
                    SalesOrderId = a.SalesOrderId,
                    RequiredDate = b.RequiredDate,
                    SalesMaterialId = b.MaterialId,
                    TotalQty = b.TotalQty,
                    MoCode = a.MoCode,
                    MoId = a.MoId,
                    Qty = a.Qty,
                    DeadLine = a.DeadLine,
                    ProductsCode = a.ProductsCode,
                    MaterialId = a.MaterialId,
                    Status = a.Status,
                    FinishedQty = a.FinishedQty,
                    Level = a.Level,
                    ParentId = a.ParentId
                })
                .ToListAsync();

            var list = new List<ApsMoGroup>() { };
            // 找到所有的子工单
            foreach (var item in req.MoList)
            {
                var tmp = mos.Where(a => a.MoCode == item.MoCode).FirstOrDefault();
                if (tmp == null) continue;
                //var tmps = await GetChildMos(item.MoId, mos);
                list.Add(new ApsMoGroup
                {
                    SalesMaterialId = item.SalesMaterialId,
                    SalesOrderId = item.SalesOrderId,
                    Mos = new List<ApsMo>() { tmp },
                });
            }

            var preSchedules = await this.FindMoRoute(list);
            //找到这些工单的上级工单的排程用来计算节拍

            var supMos = Db.Queryable<Aps_Mo>()
                .LeftJoin<Aps_Mo>((a, b) => a.MoId == b.ParentId)
                .LeftJoin<Aps_ScheduleTask>((a, b, c) => a.MoCode == c.MoCode)
                  .Where((a, b, c) => salesCodes.Contains(b.MoCode))
                  .Where((a, b, c) => a.IsValid == 1 && b.IsValid == 1 && c.IsValid == 1)
                  .Select((a, b, c) => c)
                  .ToList();


            preSchedules = preSchedules.OrderBy(a => a.MoSeq).ThenBy(a => a.StepSequence).ToList();

            var result = await CalculateSchedule(preSchedules, supMos);

            // 拿到数据以后进行处理返回前端显示
            var GanttTable = new ReqGanttTableCalculate();
            var groups = Db.Set<Bas_GroupConfig>().Where(a => a.IsValid == 1).ToList();
            var machine = Db.Set<Bas_Machine>().Where(a => a.IsValid == 1).ToList();


            var ScheduInfo = result.reqTask
             //.GroupBy(x => new { x.GroupId, x.MoCode, x.ProductsCode })
             .Select((x) => new ResScheduInfo
             {


                 ScheduleTaskId = x.ScheduleTaskId,
                 StartTime = x.WorkingStartTime,
                 EndTime = x.WorkingEndTime,
                 GroupId = x.MachineId,
                 GroupCode = machine.Where(a => a.MachineId == x.MachineId).FirstOrDefault()?.MachineCode,
                 GroupName = machine.Where(a => a.MachineId == x.MachineId).FirstOrDefault()?.MachineName,
                 GroupCodeName = machine.Where(a => a.MachineId == x.MachineId).FirstOrDefault()?.MachineCode + " " + machine.Where(a => a.MachineId == x.MachineId).FirstOrDefault()?.MachineName,
                 MoCode = x.MoCode,
                 Qty = x.Qty,
                 ProductsCode = x.ProductsCode,
                 DeadLine = x.DeadLine,
                 Weight = x.Weight,
                 UPH = x.UPH,
                 PreparationMinutes = x.PreparationMinutes,
                 BeforeMinutes = x.BeforeMinutes,
                 AfterMinutes = x.AfterMinutes,
                 SalesOrderId = x.SalesOrderId,
                 SalesMaterialId = x.SalesMaterialId,

             })
            .ToList();

            // 获取排程任务日期信息
            var listDetail = result.reqDate
                .Select(a => new ScheduleList
                {
                    ScheduleTaskDateId = a.ScheduleTaskDateId,
                    MoCode = a.MoCode,
                    Qty = a.Qty,
                    ScheduleVersionId = a.ScheduleVersionId,
                    StartTime = a.StartTime,
                    EndTime = a.EndTime,
                    ProductsCode = a.ProductsCode,
                    WorkDate = a.WorkDate,
                    UPH = result.reqTask.Where(x => x.ScheduleTaskId == a.ScheduleTaskId).FirstOrDefault()?.UPH,
                    GroupId = a.GroupId,
                    ScheduleTaskId = a.ScheduleTaskId,
                    MachineId = a.MachineId,
                    MachineCode = machine.Where(x => x.MachineId == a.MachineId).FirstOrDefault()?.MachineCode,
                    MachineName = machine.Where(x => x.MachineId == a.MachineId).FirstOrDefault()?.MachineName,
                    GroupCode = groups.Where(x => x.GroupId == a.GroupId).FirstOrDefault()?.GroupCode,
                    GroupName = groups.Where(x => x.GroupId == a.GroupId).FirstOrDefault()?.GroupName,
                    PreparationMinutes = result.reqTask.Where(x => x.ScheduleTaskId == a.ScheduleTaskId).FirstOrDefault()?.PreparationMinutes,
                    BeforeMinutes = result.reqTask.Where(x => x.ScheduleTaskId == a.ScheduleTaskId).FirstOrDefault()?.BeforeMinutes,
                    AfterMinutes = result.reqTask.Where(x => x.ScheduleTaskId == a.ScheduleTaskId).FirstOrDefault()?.AfterMinutes,
                })
                .ToList();



            var data = listDetail.GroupBy(a => new { a.MachineId, a.MoCode, a.WorkDate, a.ProductsCode, a.ScheduleTaskId })
              .Select(x => new ResScheduDate
              {
                  GroupId = x.Key.MachineId,
                  MoCode = x.Key.MoCode,
                  WorkDate = x.Key.WorkDate,
                  ProductsCode = x.Key.ProductsCode,
                  Qty = x.Sum(a => a.Qty),
                  ScheduleTaskId = x.Key.ScheduleTaskId,
                  Details = x.Where(a => a.WorkDate == x.Key.WorkDate && a.MoCode == x.Key.MoCode && a.MachineId == x.Key.MachineId).ToList()
              }).ToList();


            DateTime? beginTime = DateTime.Now.Date;
            DateTime? endTime = DateTime.Now.AddDays(30).Date;
            if (ScheduInfo.Count() > 0)
            {
                //beginTime = list.Min(a => a.WorkDate);

                if (listDetail.Max(a => a.WorkDate) > endTime)
                    endTime = listDetail.Max(a => a.WorkDate);
            }


            var calendar = Db.Set<Aps_MachineShift>()
                .LeftJoin<Bas_Machine>((a, b) => a.MachineId == b.MachineId && b.IsValid == 1)
                .LeftJoin<Aps_Shift>((a, b, c) => a.ShiftId == c.ShiftId && c.IsValid == 1)
                .Where((a, b) => a.WorkDate >= beginTime && a.WorkDate <= endTime)
                .Where((a, b) => a.ClockInType != 2)
                .GroupBy((a, b, c) => new { a.MachineId, a.WorkDate, c.ShiftType })
                .OrderBy(a => a.WorkDate)
                .Select((a, b, c) => new MachineShift
                {
                    MachineShiftId = a.MachineShiftId,
                    row_id = b.MachineName,
                    Hours = SqlFunc.AggregateSum(a.Hours),
                    WorkDate = a.WorkDate,
                    StartTime = SqlFunc.AggregateMin(a.StartTime),
                    EndTime = SqlFunc.AggregateMin(a.EndTime),
                    ShiftCode = c.ShiftCode,
                    ShiftName = c.ShiftName

                }).ToList();
            // 行事历休息日
            var restDays = Db.Set<Aps_PlantCalendar>()
                 .Where(x => x.DateType == "R")
                 .Where(x => x.WorkDate >= beginTime && x.WorkDate <= endTime)
                 .ToList(x => x.WorkDate);

            GanttTable.MinDate = result.reqDate.Min(a => a.WorkDate);
            GanttTable.MaxDate = result.reqDate.Max(a => a.WorkDate);
            GanttTable.task = ScheduInfo;
            GanttTable.taskDate = data;
            GanttTable.calendar = calendar;
            GanttTable.restDays = restDays;
            GanttTable.tasks = result.reqTask; // 需要保留一份，保存排程可以用到
            GanttTable.taskDates = result.reqDate;
            return GanttTable;
        }



        public async Task<ReqGanttTableCalculate> HandDeviceSchedule(ReqHandDeviceSchedule req)
        {
            if (req.TaskList.Count <= 0) throw new AppException("没有需要排程的任务，请确认");
            var tasks = req.TaskList.Select(a => a.ScheduleTaskId).ToList();


            //找到这些工单的上级工单的排程用来计算节拍
            var salesCodes = req.TaskList.Select(a => a.MoCode).ToList();

            var lots = req.TaskList.Select(a => a.LotNo).ToList();

            var version = await Db.Queryable<Aps_ScheduleVersion>()
                 .Where(a => a.IsEdit == 1)
                 .Where(a => a.IsValid == 1)
                 .FirstAsync();

            var supMos = await Db.Queryable<Aps_Mo>()
                 .LeftJoin<Aps_ScheduleTask>((a, b) => a.MoCode == b.MoCode)
                 .Where((a, b) => lots.Contains(a.MoCode) && b.ScheduleVersionId == version.ScheduleVersionId)
                 .Where((a, b) => a.IsValid == 1 && b.IsValid == 1)
                 .Select((a, b) => b)
                 .ToListAsync();

            var mos = await Db.Queryable<Aps_Mo>()
                .Where(a => salesCodes.Contains(a.MoCode))
                .Where(a => a.IsValid == 1)
                .ToListAsync();

            var routeDeviceIds = req.TaskList.Select(a => a.RouteDeviceId).ToList();
            var routeDevices = await Db.Queryable<Bas_RouteDevice>()
                .Where(a => routeDeviceIds.Contains(a.RouteDeviceId))
                .Where(a => a.IsValid == 1)
                .ToListAsync();

            var list = new List<ApsMo>() { };

            var Seq = 0;

            foreach (var item in req.TaskList)
            {
                var mo = mos.Where(a => a.MoCode == item.MoCode).FirstOrDefault();
                var routeDevice = routeDevices.Where(a => a.RouteDeviceId == item.RouteDeviceId).FirstOrDefault();

                var device = new RountsDevices()
                {
                    RouteControlId = item.RouteControlId,
                    RouteDeviceId = item.RouteDeviceId,
                    BeforeMinutes = item.BeforeMinutes,
                    PreparationMinutes = item.PreparationMinutes,
                    MachineId = item.MachineId,
                    GroupId = item.GroupId,
                    StepSequence = Seq,
                    Uph = routeDevice != null ? routeDevice.Uph : item.UPH,
                    OutputCircle = item.OutputCircle,
                    afterMinutes = item.AfterMinutes,
                    Enable = true
                };

                var moTmp = new ApsMo()
                {

                    SalesOrderId = item.SalesOrderId,
                    RequiredDate = null,
                    SalesMaterialId = item.SalesMaterialId,
                    TotalQty = 0,
                    MoCode = item.MoCode,
                    Status = mo.Status,
                    Qty = item.Qty,
                    DeadLine = mo != null ? mo.DeadLine : item.DeadLine,
                    ProductsCode = item.ProductsCode,
                    MaterialId = item.MaterialId,
                    FinishedQty = item.FinishedQty,
                    LotNo = item.LotNo,
                    Devices = new List<RountsDevices> { device }
                };
                Seq++;
                list.Add(moTmp);
            }

            var result = await this.CalculateSchedule(list, supMos);

            result.reqTask.ForEach(v => v.ScheduleVersionId = version.ScheduleVersionId);
            result.reqDate.ForEach(v => v.ScheduleVersionId = version.ScheduleVersionId);
            // 拿到数据以后进行处理返回前端显示
            var GanttTable = new ReqGanttTableCalculate();
            var groups = Db.Set<Bas_GroupConfig>().Where(a => a.IsValid == 1).ToList();
            var machine = Db.Set<Bas_Machine>().Where(a => a.IsValid == 1).ToList();

            var ScheduInfo = result.reqTask
             //.GroupBy(x => new { x.GroupId, x.MoCode, x.ProductsCode })
             .Select((x) => new ResScheduInfo
             {


                 ScheduleTaskId = x.ScheduleTaskId,
                 StartTime = x.WorkingStartTime,
                 EndTime = x.WorkingEndTime,
                 GroupId = x.MachineId,
                 GroupCode = machine.Where(a => a.MachineId == x.MachineId).FirstOrDefault()?.MachineCode,
                 GroupName = machine.Where(a => a.MachineId == x.MachineId).FirstOrDefault()?.MachineName,
                 GroupCodeName = machine.Where(a => a.MachineId == x.MachineId).FirstOrDefault()?.MachineCode + " " + machine.Where(a => a.MachineId == x.MachineId).FirstOrDefault()?.MachineName,
                 MoCode = x.MoCode,
                 Qty = x.Qty,
                 ProductsCode = x.ProductsCode,
                 DeadLine = x.DeadLine,
                 Weight = x.Weight,
                 UPH = x.UPH,
                 PreparationMinutes = x.PreparationMinutes,
                 BeforeMinutes = x.BeforeMinutes,
                 AfterMinutes = x.AfterMinutes,
                 SalesOrderId = x.SalesOrderId,
                 SalesMaterialId = x.SalesMaterialId,
                 ScheduleVersionId = x.ScheduleVersionId,
                 MachineId = x.MachineId,
                 OutputCircle = x.OutputCircle,
                 LotNo = x.LotNo,
                 IsValid = 1,

             })
            .ToList();

            // 获取排程任务日期信息
            var listDetail = result.reqDate
                .Select(a => new ScheduleList
                {
                    ScheduleTaskDateId = a.ScheduleTaskDateId,
                    MoCode = a.MoCode,
                    Qty = a.Qty,
                    ScheduleVersionId = a.ScheduleVersionId,
                    StartTime = a.StartTime,
                    EndTime = a.EndTime,
                    ProductsCode = a.ProductsCode,
                    WorkDate = a.WorkDate,
                    UPH = result.reqTask.Where(x => x.ScheduleTaskId == a.ScheduleTaskId).FirstOrDefault()?.UPH,
                    GroupId = a.GroupId,
                    ScheduleTaskId = a.ScheduleTaskId,
                    MachineId = a.MachineId,
                    ShiftType = a.ShiftType,
                    MachineCode = machine.Where(x => x.MachineId == a.MachineId).FirstOrDefault()?.MachineCode,
                    MachineName = machine.Where(x => x.MachineId == a.MachineId).FirstOrDefault()?.MachineName,
                    GroupCode = groups.Where(x => x.GroupId == a.GroupId).FirstOrDefault()?.GroupCode,
                    GroupName = groups.Where(x => x.GroupId == a.GroupId).FirstOrDefault()?.GroupName,
                    PreparationMinutes = result.reqTask.Where(x => x.ScheduleTaskId == a.ScheduleTaskId).FirstOrDefault()?.PreparationMinutes,
                    BeforeMinutes = result.reqTask.Where(x => x.ScheduleTaskId == a.ScheduleTaskId).FirstOrDefault()?.BeforeMinutes,
                    AfterMinutes = result.reqTask.Where(x => x.ScheduleTaskId == a.ScheduleTaskId).FirstOrDefault()?.AfterMinutes,


                })
                .ToList();



            var data = listDetail.GroupBy(a => new { a.MachineId, a.MoCode, a.WorkDate, a.ProductsCode, a.ScheduleTaskId })
              .Select(x => new ResScheduDate
              {
                  GroupId = x.Key.MachineId,
                  MoCode = x.Key.MoCode,
                  WorkDate = x.Key.WorkDate,
                  ProductsCode = x.Key.ProductsCode,
                  Qty = x.Sum(a => a.Qty),
                  ScheduleTaskId = x.Key.ScheduleTaskId,
                  Details = x.Where(a => a.WorkDate == x.Key.WorkDate && a.MoCode == x.Key.MoCode && a.MachineId == x.Key.MachineId).ToList()
              }).ToList();


            DateTime? beginTime = DateTime.Now.Date;
            DateTime? endTime = DateTime.Now.AddDays(30).Date;
            if (ScheduInfo.Count() > 0)
            {
                //beginTime = list.Min(a => a.WorkDate);

                if (listDetail.Max(a => a.WorkDate) > endTime)
                    endTime = listDetail.Max(a => a.WorkDate);
            }


            var calendar = Db.Set<Aps_MachineShift>()
                .LeftJoin<Bas_Machine>((a, b) => a.MachineId == b.MachineId && b.IsValid == 1)
                .LeftJoin<Aps_Shift>((a, b, c) => a.ShiftId == c.ShiftId && c.IsValid == 1)
                .Where((a, b) => a.WorkDate >= beginTime && a.WorkDate <= endTime)
                .Where((a, b) => a.ClockInType != 2)
                .GroupBy((a, b, c) => new { a.MachineId, a.WorkDate, c.ShiftType })
                .OrderBy(a => a.WorkDate)
                .Select((a, b, c) => new MachineShift
                {

                    MachineShiftId = SqlFunc.AggregateMax(a.MachineShiftId),
                    row_id = SqlFunc.AggregateMax(b.MachineName),
                    Hours = SqlFunc.AggregateSum(a.Hours),
                    WorkDate = a.WorkDate,
                    StartTime = SqlFunc.AggregateMin(a.StartTime),
                    EndTime = SqlFunc.AggregateMin(a.EndTime),
                    ShiftCode = SqlFunc.AggregateMax(c.ShiftCode),
                    ShiftName = SqlFunc.AggregateMax(c.ShiftName)

                }).ToList();
            // 行事历休息日
            var restDays = Db.Set<Aps_PlantCalendar>()
                 .Where(x => x.DateType == "R")
                 .Where(x => x.WorkDate >= beginTime && x.WorkDate <= endTime)
                 .ToList(x => x.WorkDate);

            GanttTable.MinDate = result.reqDate.Min(a => a.WorkDate);
            GanttTable.MaxDate = result.reqDate.Max(a => a.WorkDate);
            GanttTable.task = ScheduInfo;
            GanttTable.taskDate = data;
            GanttTable.calendar = calendar;
            GanttTable.restDays = restDays;
            GanttTable.tasks = result.reqTask; // 需要保留一份，保存排程可以用到
            GanttTable.taskDates = result.reqDate;
            return GanttTable;
        }


        // 保存排程
        public async Task<string> SaveSchedule(ResFirstCulationDto req)
        {
            var task = req.reqTask;
            var taskDate = req.reqDate;

            var TaskMos = task.Select(a => a.MachineId).ToList();
            var DetailMos = taskDate.Select(a => a.MachineId).ToList();

            var curVersion = Db.Set<Aps_ScheduleVersion>().Where(a => a.IsEdit == 1).FirstOrDefault();


            bool inTrans = Db.Ado.Transaction != null;
            if (!inTrans) Db.Ado.BeginTran();

            try
            {
                // 删除相同的工单数据
                await Db.DoDeleteAsync<Aps_ScheduleTask>(a => TaskMos.Contains(a.MachineId) && a.ScheduleVersionId == curVersion.ScheduleVersionId, true);
                await Db.DoDeleteAsync<Aps_ScheduleTaskDate>(a => DetailMos.Contains(a.MachineId) && a.ScheduleVersionId == curVersion.ScheduleVersionId, true);

                // 插入重新计算的数据
                await Db.DoInsertAsync(task);
                await Db.DoInsertAsync(taskDate);
                if (!inTrans) Db.Ado.CommitTran();
                return "保存成功";
            }
            catch (Exception e)
            {
                if (!inTrans) Db.Ado.RollbackTran();
                throw new AppException(e.Message);
            }


        }


        public async Task<List<ApsMo>> GetChildMos(long parentId, List<ApsMo> lst)
        {
            var childMos = new List<ApsMo>();

            // Find the parent and add it to the list
            var parentItem = lst.FirstOrDefault(item => item.MoId == parentId);
            if (parentItem != null)
            {
                childMos.Add(parentItem);
            }

            foreach (var item in lst)
            {
                if (item.ParentId == parentId)
                {
                    if (!childMos.Any(mo => mo.MoId == item.MoId))
                    {
                        childMos.Add(item);
                        childMos.AddRange(await GetChildMos(item.MoId, lst));
                    }
                }
            }

            return childMos;
        }

        public async Task<string> GetClendar(ReqApsMachineShift Machinshift)
        {
            bool RestartSet = false;

            if (Machinshift.deviceIds == null || Machinshift.deviceIds.Count == 0)
            {
                Machinshift.deviceIds = Db.Set<Bas_Machine>().Select(a => a.MachineId).ToList();
                RestartSet = true;
            }

            DateTime StartTime = Convert.ToDateTime(Machinshift.StartTime);
            DateTime EndTime = Convert.ToDateTime(Machinshift.EndTime);

            var deviceDates = Db.Set<Aps_MachineCalendar>()
                    .Where(a => a.MachineId != null)
                    .ToList();

            // 获取原始数据
            var shiftDetailsWithCalendars = Db.Set<Aps_ShiftDetail>()
                .LeftJoin<Aps_PlantCalendar>((a, b) => a.ShiftId == b.ShiftId)
                .Where((a, b) => b.IsValid == 1)
                .Select((a, b) => new
                {
                    a.ShiftId,
                    a.ClockInType,
                    a.StartType,
                    a.EndType,
                    a.Hours,
                    a.ShiftDetailId,
                    WorkDate = b.WorkDate,
                    StartTime = a.StartTime,
                    EndTime = a.EndTime
                })
                .ToList();

            // 处理数据
            var plantShifts = shiftDetailsWithCalendars.Select(item =>
            {

                var startTime = Convert.ToDateTime(item.WorkDate.AddDays(item.StartType).ToString("yyyy-MM-dd") + " " + item.StartTime.ToString("HH:mm:ss"));
                var endTime = Convert.ToDateTime(item.WorkDate.AddDays(item.EndType).ToString("yyyy-MM-dd") + " " + item.EndTime.ToString("HH:mm:ss"));



                return new Aps_MachineShift
                {
                    ShiftId = item.ShiftId,
                    ClockInType = item.ClockInType,
                    StartType = item.StartType,
                    EndType = item.EndType,
                    Hours = item.Hours,
                    // ShiftDetailId = item.ShiftDetailId,
                    StartTime = (DateTime)startTime,
                    EndTime = (DateTime)endTime,
                    WorkDate = (DateTime)item.WorkDate,
                };
            }).OrderBy(a => a.StartTime).ToList();

            var MachineShift = new List<Aps_MachineShift>();

            foreach (var deviceId in Machinshift.deviceIds)
            {
                var items = CalculateClendar(deviceDates, plantShifts, deviceId);

                var currentClendar = JsonConvert.DeserializeObject<List<Aps_MachineShift>>(JsonConvert.SerializeObject(items));
                currentClendar.ForEach(i => i.MachineId = deviceId);
                MachineShift.AddRange(currentClendar);
            }


            // 先删除之前的旧的设备数据，如果前端没有传设备id，说明当前所以设备重新计算

            if (RestartSet)
            {
                await Db.Deleteable<Aps_MachineShift>()
                                              .Where(x => x.StartTime >= StartTime && x.EndTime <= EndTime)
                                              .ExecuteCommandAsync();
            }
            else
            {
                await Db.Deleteable<Aps_MachineShift>()
                                       .Where(x => x.StartTime >= StartTime && x.EndTime <= EndTime && Machinshift.deviceIds.Contains((long)x.MachineId))
                                       .ExecuteCommandAsync();
            }




            MachineShift = MachineShift.Where(a => a.StartTime >= StartTime && a.EndTime <= EndTime).ToList();

            // 插入到Aps_MachineShift 表中
            await Db.DoInsertAsync(MachineShift);
            return "";
        }

        public List<Aps_MachineShift> GenerateMachineShift(long? deviceId)
        {
            var deviceDates = Db.Set<Aps_MachineCalendar>()
                    .Where(a => a.MachineId != null)
                    .ToList();

            var plantShifts = Db.Set<Aps_ShiftDetail>().OrderBy(a => a.StartTime).ToList();
            var machineShift = JsonConvert.DeserializeObject<List<Aps_MachineShift>>(JsonConvert.SerializeObject(plantShifts));

            return CalculateClendar(deviceDates, machineShift, deviceId);
        }

        /// <summary>
        /// 生成设备班表明细
        /// </summary>
        /// <param name="deviceDates">设备</param>
        /// <param name="plantShifts"></param>
        /// <param name="deviceId"></param>
        /// <returns></returns>
        private List<Aps_MachineShift> CalculateClendar(List<Aps_MachineCalendar> deviceDates, List<Aps_MachineShift> plantShifts, long? deviceId)
        {
            plantShifts = plantShifts.OrderBy(a => a.StartTime).ToList();
            var deviceShift = new List<Aps_MachineShift> { };
            //找到设备维护的时间段
            var deviceDatesTmp = deviceDates.Where(a => a.MachineId == deviceId).OrderBy(a => a.StartTime).ToList();
            if (deviceDatesTmp.Count <= 0) return plantShifts;

            foreach (var ds in plantShifts)
            {
                var currentClendar = JsonConvert.DeserializeObject<Aps_MachineShift>(JsonConvert.SerializeObject(ds));

                //休息时间
                if (currentClendar.ClockInType == 2)
                {
                    deviceShift.Add(currentClendar);
                    continue;
                }

                var dayDeviceDates = deviceDatesTmp.Where(a => a.StartTime.Value.Date == currentClendar.StartTime.Date || a.EndTime.Value.Date == currentClendar.EndTime.Date).OrderBy(a => a.StartTime).ToList();





                if (dayDeviceDates.Count <= 0)
                {

                    deviceShift.Add(currentClendar);
                    continue;
                }
                if (dayDeviceDates[dayDeviceDates.Count - 1].EndTime <= currentClendar.StartTime || dayDeviceDates[0].StartTime >= currentClendar.EndTime)
                {

                    deviceShift.Add(currentClendar);
                    continue;
                }
                //为提升速度如果设备行事历和工厂行事历无交集则返回


                var result = CalculateDeviceItem(dayDeviceDates, currentClendar);
                if (result == null || result.Count <= 0)
                {
                    deviceShift.Add(ds);
                }
                else
                {
                    deviceShift.AddRange(result);
                }

            }
            return deviceShift;

        }


        private List<Aps_MachineShift> CalculateDeviceItem(List<Aps_MachineCalendar> dayDeviceDates, Aps_MachineShift currentClendar)
        {

            var deviceShift = new List<Aps_MachineShift> { };
            int deviceIndex = 0;



            foreach (var d in dayDeviceDates)
            {
                deviceIndex++;

                var shift1 = new Aps_MachineShift

                {
                    MachineShiftId = currentClendar.MachineShiftId,
                    ClockInType = currentClendar.ClockInType,
                    StartType = currentClendar.StartType,
                    EndType = currentClendar.EndType,
                    WorkDate = currentClendar.WorkDate,
                    ShiftId = currentClendar.ShiftId,

                };
                var shift2 = new Aps_MachineShift
                {
                    MachineShiftId = currentClendar.MachineShiftId,
                    ClockInType = currentClendar.ClockInType,
                    StartType = currentClendar.StartType,
                    EndType = currentClendar.EndType,
                    WorkDate = currentClendar.WorkDate,
                    ShiftId = currentClendar.ShiftId,

                };



                //设备行事历和工厂行事历没有交集
                if (d.StartTime >= currentClendar.EndTime || d.EndTime <= currentClendar.StartTime)
                {
                    continue;
                }
                //设备行事历开始结束时间在工厂行事历之内
                if (currentClendar.StartTime < d.StartTime && d.EndTime < currentClendar.EndTime)
                {
                    shift1.StartTime = (DateTime)currentClendar.StartTime;
                    shift1.EndTime = (DateTime)d.StartTime;
                    deviceShift.Add(shift1);
                    shift2.StartTime = (DateTime)d.EndTime;
                    shift2.EndTime = (DateTime)currentClendar.EndTime;
                    if (deviceIndex != dayDeviceDates.Count && dayDeviceDates[deviceIndex].StartTime < currentClendar.EndTime)
                    {
                        currentClendar = shift2;

                    }
                    else
                    {
                        deviceShift.Add(shift2);
                    }

                }
                // 设备行事历开始时间小于等于工厂行事历开始时间，设备结束时间在工厂行事历之内
                if (d.StartTime <= currentClendar.StartTime && d.EndTime > currentClendar.StartTime && d.EndTime < currentClendar.EndTime)
                {
                    shift1.StartTime = (DateTime)d.EndTime;
                    shift1.EndTime = (DateTime)currentClendar.EndTime;
                    currentClendar = shift1;

                    if (deviceIndex != dayDeviceDates.Count && dayDeviceDates[deviceIndex].StartTime < currentClendar.EndTime)
                    {
                        currentClendar = shift1;
                    }
                    else
                    {
                        deviceShift.Add(shift1);
                    }

                }
                // 设备行事历开始时间在工厂行事历之内，设备结束时间大于等于工厂结束时间
                if (d.StartTime > currentClendar.StartTime && d.StartTime < currentClendar.EndTime && d.EndTime >= currentClendar.EndTime)
                {
                    shift1.StartTime = currentClendar.StartTime;
                    shift1.EndTime = (DateTime)d.StartTime;

                    currentClendar = shift1;
                    deviceShift.Add(shift1);


                }


                var a = deviceShift;
            }
            deviceShift.ForEach(i => i.Hours = (int)(i.EndTime - i.StartTime).TotalMinutes);


            return deviceShift;
        }


        /// <summary>
        /// 计算时间
        /// </summary>
        /// <param name="device"></param>
        /// <param name="task"></param>
        /// <param name="begintime"></param>
        /// <param name="lstCalendar"></param>
        /// <param name="shiftDetails"></param>
        /// <param name="devices"></param>
        /// <returns></returns>
        public ResCulationLineTimeDto CalculationDeviceTime(RountsDevices device, Aps_ScheduleTask task, DateTime begintime, List<Aps_PlantCalendar> lstCalendar, List<Aps_MachineShift> shiftDetails, List<RountsDevices> devices = null)
        {
            ResCulationLineTimeDto res = new ResCulationLineTimeDto();
            res.IsClendarFinish = 1;
            res.Surplus = 0;
            var lstTaskDate = new List<Aps_ScheduleTaskDate>();
            //剩余数量
            var surplus = (decimal)task.Qty - (decimal)(task.FinishedQty ?? 0);

            if (task.MoCode == "STC3401")
            {
                var i = 0;
            }

            var uphPerMinutes = surplus / task.UPH / 60;
            //需要时间
            decimal needMinutes = 0;


            //var shiftDetails = device.MachineShifts;
            var machineShifts = shiftDetails.Where(a => a.MachineId == device.MachineId).ToList();
            var shiftCaches = new List<Aps_MachineShift>();

            foreach (var calendar in lstCalendar)
            {

                if (surplus <= 0) break;

                //判断是否超期，超期则放到其它设备生产，如果没有其它设备则全部排在此设备
                // 检查后面的设备有没有班表信息，没有则排在此设备直到排完

                //if (calendar.WorkDate >= task.DeadLine)
                //{

                //    var nextDevices = devices.Where(a => a.RouteDeviceId != device.RouteDeviceId && a.Enable).Select(a => a.MachineId).ToList();
                //    var nextShifts = shiftDetails.Where(a => nextDevices.Contains(a.MachineId)).ToList();
                //    if (nextDevices.Count > 0 && nextShifts.Count > 0)
                //    {
                //        break;
                //    }
                //}

                //获取班表的时间顺序
                var lstScheduleDetail = machineShifts.Where(a => a.ShiftId == calendar.ShiftId && a.WorkDate == calendar.WorkDate).ToList();

                //此处的大于等于号决定了工单甘特图是否连续
                lstScheduleDetail = lstScheduleDetail.Where(x => x.EndTime >= begintime).OrderBy(x => x.StartTime).ToList();
                if (lstScheduleDetail.Count == 0) continue;

                decimal allMinutes = 0;

                // 获取当前的工艺设备，如果类型是1则按UPH计算，如果是类型是2则按固定时间算
                var DeviceTypeInfo = Db.Set<Bas_RouteDevice>()
                           .Where(a => a.RouteDeviceId == task.RouteDeviceId)
                           .FirstOrDefault();


                if (DeviceTypeInfo.Type == "2")
                {
                    //固定时长不管数量多少都需要做这么多的时间（烧结炉）
                    needMinutes = surplus / (decimal)uphPerMinutes;

                }
                else
                {
                    needMinutes = surplus / (decimal)task.UPH * 60;
                }


                //剩余数量需要多久才能做完
                // needMinutes = surplus / (decimal)task.UPH * 60;

                //创建排程时间
                var aps = new Aps_ScheduleTaskDate();
                aps.ScheduleTaskId = task.ScheduleTaskId;
                aps.ScheduleVersionId = task.ScheduleVersionId;
                aps.MachineId = device.MachineId;
                aps.MoCode = task.MoCode;
                aps.ProductsCode = task.ProductsCode;
                aps.ShiftId = calendar.ShiftId;
                aps.ShiftType = calendar.ShiftType;
                aps.WorkDate = calendar.WorkDate;
                aps.DateType = calendar.DateType;
                aps.StartTime = begintime;
                aps.GroupId = task.GroupId;

                //decimal qty = 0;
                decimal hours = 0;
                var deadMoMinutes = (decimal)new TimeSpan(task.DeadLine.Value.Ticks - begintime.Ticks).TotalMinutes;

                foreach (var item in lstScheduleDetail)
                {
                    DateTime? startTime = null;
                    decimal minutes = item.Hours;
                    if (item.StartTime < begintime)
                        minutes = (decimal)new TimeSpan(item.EndTime.Ticks - begintime.Ticks).TotalMinutes;
                    if (minutes == 0) continue;


                    if (minutes >= needMinutes)
                    {
                        //累计工时
                        hours = hours + needMinutes;
                        //下次开始时间
                        startTime = item.StartTime > begintime ? item.StartTime : begintime;
                        begintime = (item.StartTime > begintime ? item.StartTime : begintime).AddMinutes((double)needMinutes);

                        needMinutes = 0;

                        //结束时段循环
                        break;
                    }
                    else
                    {
                        //下次开始时间
                        begintime = item.EndTime;
                        //累计工时
                        hours = hours + minutes;
                        //剩余工时
                        needMinutes = needMinutes - minutes;

                        startTime = item.StartTime;
                    }
                }

                aps.EndTime = begintime;
                aps.Hours = hours;

                if (needMinutes == 0)
                {
                    aps.Qty = (decimal?)surplus;
                    surplus = 0;
                }
                else
                {

                    aps.Qty = DeviceTypeInfo.Type == "2" ? hours * uphPerMinutes : Math.Round(hours * (decimal)task.UPH / 60);
                    surplus = surplus - (decimal)aps.Qty;
                }

                if (aps.Qty > 0 && aps.Hours > 0)
                    lstTaskDate.Add(aps);
            }

            if (surplus > 0)
                res.IsClendarFinish = 1;

            res.beginTime = begintime;
            res.taskDates = lstTaskDate;
            res.Surplus = surplus;
            return res;
        }



        public class ScheduleItem
        {
            public DateTime StartDateTime { get; set; }
            public DateTime EndDateTime { get; set; }

            public ScheduleItem(DateTime start, DateTime end)
            {
                StartDateTime = start;
                EndDateTime = end;
            }
        }

        public class FactoryScheduleManager
        {
            private static List<ScheduleItem> SplitIfOverlaps(ScheduleItem factoryItem, ScheduleItem maintenanceItem)
            {
                List<ScheduleItem> result = new List<ScheduleItem>();

                if (factoryItem.StartDateTime >= maintenanceItem.StartDateTime && factoryItem.EndDateTime <= maintenanceItem.EndDateTime)
                {
                    // 工厂时间段完全被设备维护时间段包含，不返回任何值
                    return result;
                }
                else if (factoryItem.StartDateTime < maintenanceItem.StartDateTime && factoryItem.EndDateTime > maintenanceItem.StartDateTime)
                {
                    // 设备维护时间段从工厂时间段内部开始
                    result.Add(new ScheduleItem(factoryItem.StartDateTime, maintenanceItem.StartDateTime));
                    if (factoryItem.EndDateTime > maintenanceItem.EndDateTime)
                    {
                        result.Add(new ScheduleItem(maintenanceItem.EndDateTime, factoryItem.EndDateTime));
                    }
                }
                else
                {
                    // 不重叠的情况，直接返回原始的工厂时间段
                    result.Add(factoryItem);
                }

                return result;
            }

            public static List<ScheduleItem> SplitFactorySchedule(ScheduleItem factoryItem, List<ScheduleItem> maintenanceItems)
            {
                var finalResult = new List<ScheduleItem>();
                var currentFactoryItem = factoryItem;
                int index = 0;
                foreach (var maintenanceItem in maintenanceItems)
                {
                    index++;
                    // 对于每一个设备维护时间段，分割工厂时间段
                    var splitResult = SplitIfOverlaps(currentFactoryItem, maintenanceItem);

                    // 如果分割结果不为空，则更新当前工厂时间段

                    if (splitResult.Count > 0)
                    {
                        var tmp = splitResult.OrderByDescending(item => item.StartDateTime).FirstOrDefault(item => item != null);
                        currentFactoryItem = tmp == null ? factoryItem : tmp;

                        if (splitResult.Count > 1 && index != maintenanceItems.Count)
                        {
                            finalResult.Add(splitResult[0]);
                        }
                        else
                        {
                            finalResult.AddRange(splitResult);
                        }
                    }
                }

                // 返回最终分割后的工厂时间段
                return finalResult.Distinct().OrderBy(x => x.StartDateTime).ToList();
            }
        }

        public JObject test()
        {
            var factoryItem = new ScheduleItem(new DateTime(2024, 8, 1, 8, 0, 0), new DateTime(2024, 8, 10, 17, 0, 0));
            var maintenanceItems = new List<ScheduleItem>
    {
        new ScheduleItem(new DateTime(2024, 8, 3, 8, 0, 0), new DateTime(2024, 8, 5, 17, 0, 0)),
        new ScheduleItem(new DateTime(2024, 8, 6, 8, 0, 0), new DateTime(2024, 8, 7, 17, 0, 0)),
          new ScheduleItem(new DateTime(2024, 8, 8, 8, 0, 0), new DateTime(2024, 8, 9, 17, 0, 0))
            };

            var result = FactoryScheduleManager.SplitFactorySchedule(factoryItem, maintenanceItems);

            return (JObject.FromObject(result));
        }

    }










}